/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Executable for image conversion.                              *
 * modul:       convert.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stddef.h>
#include <stdio.h>

#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "typedfs.h"
#include "image.h"
#include "imageop.h"
#include "mor_tool.h"
#include "img_tool.h"
#include "quantise.h"

#include <matrix.h>


/////////////////////Main CODE starts here/////////////////////////////////

char Verbosity = 0;

void PrintHeader(void)
{
char HdrPrinted=0;
  if(Verbosity>=0 && HdrPrinted==0)
  {
    printf("\n\r <<< GCONVERT >>> Convert picture format (c)1997-2025 F&TSoft, version %u.%u\n", (RAS_IMG_VERSION>>8), (RAS_IMG_VERSION & 0xFF));
    HdrPrinted = 1;
  }
}


Raster2DAbstract *ResizeRaster(Raster2DAbstract *RIn, uint16_t Size1D, uint16_t Size2D)
{
  if(RIn==NULL || RIn->Data2D==NULL || RIn->Size1D==0 || RIn->Size2D==0) return RIn;

  if(RIn->Channels()==3 || RIn->Channels()==4)
  {
    Raster2DAbstract *ROut = ((RIn->Channels()==4) ?
		CreateRaster2DRGBA(Size1D,Size2D,RIn->GetPlanes()/4) : CreateRaster2DRGB(Size1D,Size2D,RIn->GetPlanes()/3));
    if(ROut==NULL) goto AllocationProblem;
    RGBQuad RGB;
    for(unsigned y=0; y<Size2D; y++)
    {
      Raster1DAbstract *ROut1 = ROut->GetRowRaster(y);
      if(ROut1==NULL) continue;
      Raster1DAbstract *RIn1 = RIn->GetRowRaster(((unsigned)RIn->Size2D*y)/Size2D);
      if(RIn1==NULL) continue;

      for(unsigned x=0; x<Size1D; x++)
      {
        RIn1->Get(((unsigned)RIn->Size1D*x)/Size1D,&RGB);
        ROut1->Set(x,&RGB);
      }
    }
    return ROut;
  }

  if(RIn->GetPlanes() < 0)
  {
    Raster2DAbstract *ROut = CreateRaster2D(Size1D,Size2D,RIn->GetPlanes());
    if(ROut==NULL) goto AllocationProblem;
    for(unsigned y=0; y<Size2D; y++)
    {
      Raster1DAbstract *ROut1 = ROut->GetRowRaster(y);
      if(ROut1==NULL) continue;
      Raster1DAbstract *RIn1 = RIn->GetRowRaster(((unsigned)RIn->Size2D*y)/Size2D);
      if(RIn1==NULL) continue;

      for(unsigned x=0; x<Size1D; x++)
      {      
        ROut1->SetValue1Dd(x, RIn1->GetValue1Dd(((unsigned)RIn->Size1D*x)/Size1D));
      }
    }
    return ROut;
  }

  {
    Raster2DAbstract *ROut = CreateRaster2D(Size1D,Size2D,RIn->GetPlanes());
    if(ROut==NULL) goto AllocationProblem;
    for(unsigned y=0; y<Size2D; y++)
    {
      Raster1DAbstract *ROut1 = ROut->GetRowRaster(y);
      if(ROut1==NULL) continue;
      Raster1DAbstract *RIn1 = RIn->GetRowRaster(((unsigned)RIn->Size2D*y)/Size2D);
      if(RIn1==NULL) continue;

      for(unsigned x=0; x<Size1D; x++)
      {      
        ROut1->SetValue1D(x, RIn1->GetValue1D(((unsigned)RIn->Size1D*x)/Size1D));
      }
    }
    return ROut;
  }

AllocationProblem:
  printf("Image allocation error %u x %u\n",Size1D,Size2D);
  return RIn;
}


void PrintHelp(void)
{
TImageFileHandler *LoadPicture_XXX;

  printf("Supported file formats are:\n");
  for(LoadPicture_XXX=TImageFileHandler::first();LoadPicture_XXX!=NULL;LoadPicture_XXX=LoadPicture_XXX->next())
  {
    if(LoadPicture_XXX->Flags & FILE_FORMAT_DUPLICATE) continue;
    printf("%s%s%s %s%s; ",LoadPicture_XXX->extension(),
			LoadPicture_XXX->description()==NULL?"":"#",
			LoadPicture_XXX->description()==NULL?"":LoadPicture_XXX->description(),
			LoadPicture_XXX->LoadPicture==NULL?"":"R",
			LoadPicture_XXX->SavePicture==NULL?"":"W");
  }
  printf(//"\n/NoScale      - means truncating image data"
	       "\n/Colors n     - reduce amount of colors"
	       "\n/D | /Dilatation - Morphology dilatation"
	       "\n/E | /Erosion - Morphology erosion"
	       "\n/El4 | /El8   - Morphology element"
	       "\n/Format \"str\" - specify output format"
	       "\n/Gaus n       - Gaussian filter"
	       "\n/Gray         - discards all color informations"
	       "\n/Help         - display help, alternative '/help operators' '/help formats'"
	       "\n/Invert       - inverts image"
	       "\n/Separate[R|G|B|IDX] - remove one channel from true color images"
	       "\n/PeelPlane n  - cut only plane with number n"
	       "\n/PeelFrame n  - cut only scene with number n"
               "\n/Quantise-Delta - use more complex and slower oct tree delta based algorithm."
               "\n/Quantise-Fast - use fast and not reliable quantisation algorithm."
               "\n/Threshold n  - theshold image and generate binary output"
	       "\n/Resize factor%% - resize all rasters with given factor, or geometry like 60x50"
	       "\n/s            - decrease verbosity level"
	       "\n/v            - increase verbosity level"
	       "\n");
  printf("Speciffic encoder options:\n"
	       "QUALITY nn	- set quality for JPEG writer.\n"
	       "\n");
}


void PrintHelpFileFormats(void)
{
TImageFileHandler *LoadPicture_XXX;

  printf("Supported image formats:\n");
  for(LoadPicture_XXX=TImageFileHandler::first();LoadPicture_XXX!=NULL;LoadPicture_XXX=LoadPicture_XXX->next())
  {
    printf("%s%s%s %s%s; ",LoadPicture_XXX->extension(),
			LoadPicture_XXX->description()==NULL?"":"#",
			LoadPicture_XXX->description()==NULL?"":LoadPicture_XXX->description(),
			LoadPicture_XXX->LoadPicture==NULL?"":"R",
			LoadPicture_XXX->SavePicture==NULL?"":"W");
    if(LoadPicture_XXX->Flags & FILE_FORMAT_CAP_EXIF) printf("+EXIF");
    if(LoadPicture_XXX->Flags & FILE_FORMAT_256_COLORS) printf("-MAX256Colors");
    if(LoadPicture_XXX->Flags & FILE_FORMAT_DUPLICATE) printf("(duplicated)");
    printf("\n");
  }
}


void PrintHelpOperators(void)
{
  printf("Image manipulation operators:\n");
  printf(
        "\n/Canny [factor] - Canny edge detector."
        "\n/Diff        - Calculates difference in X and Y directions."
        "\n/DiffX        - Calculates difference in X direction."
        "\n/DiffY        - Calculates difference in Y direction."
	"\n/Gaus n       - Gaussian filter"
	"\n/Gray         - discards all color informations"
	"\n/Invert       - inverts image"
        "\n/Mask n       - filter with a given mask n*n."
        "\n/Median n     - filter using median filter n*n"
	"\n/PeelPlane n  - cut only plane with number n"
	"\n/PeelFrame n  - cut only scene with number n"
        "\n/Quantise-Delta - use more complex and slower oct tree delta based algorithm."
        "\n/Quantise-Fast - use fast and not reliable quantisation algorithm."
	"\n/Resize factor%% - resize all rasters with given factor, or geometry like 60x50"
	"\n/Separate[R|G|B|IDX] - remove one channel from true color images"
        "\n/Threshold n  - theshold image and generate binary output"
	"\n");
}


const TImageFileHandler *FindImgHandler(const char *OutFormat)
{
const TImageFileHandler *SavePicture_XXX;
  for(SavePicture_XXX=TImageFileHandler::first(); SavePicture_XXX!=NULL; SavePicture_XXX=SavePicture_XXX->next())
  {
    if(SavePicture_XXX->SavePicture!=NULL)
    {
      const char *ext = SavePicture_XXX->extension();

      if(ext!=NULL)
      {
        if(*ext=='.') ext++;
	size_t i = strlen(ext);
        if(!stricmp(OutFormat,ext))	// Exact match
        {
          return SavePicture_XXX;
	}
	if(!strnicmp(OutFormat,ext,i))
        {
	  if(OutFormat[i]=='#')
	  {
	    if(!stricmp(OutFormat+i+1,SavePicture_XXX->description()))
	    {
              return SavePicture_XXX;
	    }
	  }
        }
      }
    }
  }
  return NULL;
}


/////////////////////////////////////////////

class AImageFilter
{
public:
  AImageFilter(void);
  virtual ~AImageFilter();
  virtual void ProcessFilter(Image *Im) = 0;

  AImageFilter *Next;
  static AImageFilter *First;
  static void Rewind(void);
};

 AImageFilter *AImageFilter::First = NULL;

AImageFilter::AImageFilter()
{
  if(First==NULL)
  {
    First = this;
    Next = NULL;
  }
  else
  {
    Next=First;
    First=this;
  }
}

AImageFilter::~AImageFilter()
{
  if(First==this)
  {
    First = Next;
    return;
  }
}


void AImageFilter::Rewind(void)
{
  AImageFilter *NewFirst = NULL;
  while(First!=NULL)
  {
    AImageFilter * const Curr = First;
    First = First->Next;
    Curr->Next = NewFirst;
    NewFirst = Curr;
  }
  First = NewFirst;
}

//////////////////////////////////

class GrayImageFilter: AImageFilter
{
public:
  virtual void ProcessFilter(Image *Im);
};

void GrayImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rConverting to gray ");
 while(Im != NULL)
 {
   Convert2Gray(Im);
   Im->AttachPalette(NULL);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class InvertImageFilter: AImageFilter
{
public:
  virtual void ProcessFilter(Image *Im);
};

void InvertImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rInverting ");
 while(Im != NULL)
 {
   if(Im->Palette)
   {
     NotR((char*)Im->Palette->Data1D, (Im->Palette->Size1D*Im->Palette->GetPlanes()+7)/8);
   }
   else
   {
     if(Im->Raster && Im->Raster->GetPlanes()>0)
     {
       for(unsigned y=0; y<Im->Raster->Size2D; y++)
       {
         NotR((char*)Im->Raster->GetRow(y), (Im->Raster->Size1D*Im->Raster->GetPlanes()+7)/8);
       }
     }
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class ThresholdImageFilter: AImageFilter
{
public:
  ThresholdImageFilter(unsigned NewThrValue): ThrValue(NewThrValue) {}

  unsigned ThrValue;
  virtual void ProcessFilter(Image *Im);
};

void ThresholdImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rThreshold %u ", ThrValue);
 
 Image Im2 = Threshold(Im,ThrValue);
 Image *itIm2 = &Im2;
 while(itIm2 != NULL)
 {
   Im->AttachRaster(itIm2->Raster);
   Im->AttachPalette(NULL);
   if(Im->Next != NULL) Im=Im->Next;
   itIm2 = itIm2->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class FlipImageFilter: AImageFilter
{
public:
  virtual void ProcessFilter(Image *Im);
};

void FlipImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rFlip ");
 while(Im != NULL)
 {
   if(Im->Raster)
     Flip1D(Im->Raster);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


class FlopImageFilter: AImageFilter
{
public:
  virtual void ProcessFilter(Image *Im);
};

void FlopImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rFlip ");
 while(Im != NULL)
 {
   if(Im->Raster)
     Flip1D(Im->Raster);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////

class DilateImageFilter: AImageFilter
{
public:
  DilateImageFilter(uint16_t new_MorphType=El8): MorphType(new_MorphType) {};
  virtual void ProcessFilter(Image *Im);

  uint16_t MorphType;
};


void DilateImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rDilatation ");
 while(Im != NULL)
 {
   Dilatation(Im->Raster, MorphType);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}

//////////////////////////////////

class ErodeImageFilter: AImageFilter
{
public:
  ErodeImageFilter(uint16_t new_MorphType=El8): MorphType(new_MorphType) {};
  virtual void ProcessFilter(Image *Im);

  uint16_t MorphType;
};


void ErodeImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rErosion ");
 while(Im != NULL)
 {
   Erosion(Im->Raster, MorphType);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////

class ResizePercentImageFilter: AImageFilter
{
public:
  ResizePercentImageFilter(float NewPercent): Percent(NewPercent) {};
  virtual void ProcessFilter(Image *Im);

  float Percent;
};


void ResizePercentImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rResize %f[%] ", Percent);
 while(Im != NULL)
 {
   if(Im->Raster->Data2D!=NULL)
   {
     Im->AttachRaster(ResizeRaster(Im->Raster, Im->Raster->Size1D*Percent/100, Im->Raster->Size2D*Percent/100));
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////

class ResizeGeomImageFilter: AImageFilter
{
public:
  ResizeGeomImageFilter(const int NEWxSize, const int NEWySize): xSize(NEWxSize), ySize(NEWySize) {};
  virtual void ProcessFilter(Image *Im);

  int xSize, ySize;
};


void ResizeGeomImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rResize %dx%d ", xSize,ySize);
 while(Im != NULL)
 {
   if(Im->Raster->Data2D!=NULL)
   {
     Im->AttachRaster(ResizeRaster(Im->Raster,xSize,ySize));
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class PeelPlaneImageFilter: AImageFilter
{
public:
  PeelPlaneImageFilter(int IniPlaneNum): PlaneNum(IniPlaneNum) {};
  virtual void ProcessFilter(Image *Im);

  int PlaneNum;
};


void PeelPlaneImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rPeel plane %d ",PlaneNum);
 while(Im != NULL)
 {
   if(Im->Raster!=NULL && Im->Raster->Data2D!=NULL)
   {
     *Im = PeelPlane(*Im, PlaneNum);
     Im->AttachPalette(NULL);
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class SeparateChannelImageFilter: AImageFilter
{
public:
  SeparateChannelImageFilter(int IniChanNum): ChannelNum(IniChanNum) {};
  virtual void ProcessFilter(Image *Im);

  int ChannelNum;
};


void SeparateChannelImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rSeparate channel %d ",ChannelNum);
 while(Im != NULL)
 {
   if(ChannelNum>=0)
   {
     if(ChannelNum!=10)
      *Im = Peel8bit(*Im, ChannelNum);	// R,G,B must be separated BEFORE palatte is cut.

     Im->AttachPalette(NULL);
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class MaskImageFilter: AImageFilter
{
public:
  MaskImageFilter(int IniMaskSize): MaskSize(IniMaskSize) {};
  virtual void ProcessFilter(Image *Im);

  int MaskSize;
};


void MaskImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rMask filter %u ",MaskSize);
 if(Im != NULL)
 {
   *Im = Mask(Im, MaskSize);
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class MedianImageFilter: AImageFilter
{
public:
  MedianImageFilter(int IniMaskSize): MedianSize(IniMaskSize) {};
  virtual void ProcessFilter(Image *Im);

  int MedianSize;
};


void MedianImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rMedian filter %u ",MedianSize);
 while(Im != NULL)
 {
   if(MedianSize>=0)
   {    
     *Im = Median(*Im, MedianSize);
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class ColorsImageFilter: AImageFilter
{
public:
  ColorsImageFilter(int IniColors, uint8_t Alg=0): Colors(IniColors), Algorithm(Alg) {};
  virtual void ProcessFilter(Image *Im);
  bool CheckColors(const Raster2DAbstract *Ras) const;

  unsigned Colors;
  uint8_t Algorithm;
};


bool ColorsImageFilter::CheckColors(const Raster2DAbstract *Ras) const
{
  if(Ras==NULL) return false;

  if(Colors>=65536 && Ras->GetPlanes()<=16) return false;
  if(Colors>=256 && Ras->GetPlanes()<=8) return false;
  if(Colors>=16 && Ras->GetPlanes()<=4) return false;
  if(Colors>=4 && Ras->GetPlanes()<=2) return false;
  if(Colors>=2 && Ras->GetPlanes()<=1) return false;

return true;
}


void ColorsImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rReduce colors %u ",Colors);
 while(Im != NULL)
 {
   if(CheckColors(Im->Raster))
   {
     APalette *Pal = (Algorithm==0)?FindIndices(Im->Raster,Colors):FindIndices2(Im->Raster,Colors);
     if(Pal)
     {
       Raster2DAbstract *Ras = Remap2Palette(Pal,Im->Raster);
       if(Ras)
       {
         Im->AttachRaster(Ras);
         Im->AttachPalette(Pal);
       }
       else
       {
         printf(" Remap2Palette failed");
         delete Pal;	// Something failed.
       }
     }
     else
         printf(" FindIndices failed");     
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////

class CannyImageFilter: AImageFilter
{
public:
  CannyImageFilter(uint8_t NewScale): Scale(NewScale) {};
  virtual void ProcessFilter(Image *Im);

  uint8_t Scale;
};


void CannyImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rCanny %u", (unsigned)Scale);
 while(Im != NULL)
 {
   if(Im->Raster!=NULL && Im->Raster->Data2D!=NULL)
   {
     GausFunc(Im->Raster, gfCanny, Scale);
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class GausImageFilter: AImageFilter
{
public:
  GausImageFilter(int IniSize): GausSize(IniSize) {};
  virtual void ProcessFilter(Image *Im);

  int GausSize;
};


void GausImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rGaus filter %u ",GausSize);
 while(Im != NULL)
 {
   if(GausSize>=0)
   {
     GausFunc(Im->Raster, gfGaus, GausSize);
   }
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class DiffImageFilter: AImageFilter
{
public:
  DiffImageFilter(void) {};
  virtual void ProcessFilter(Image *Im);
};


void DiffImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rDiff filter ");
 while(Im != NULL)
 {
   GausFunc(Im->Raster, gfGausDiff, 1);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class DiffXImageFilter: AImageFilter
{
public:
  DiffXImageFilter(void) {};
  virtual void ProcessFilter(Image *Im);
};


void DiffXImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rDiffX filter ");
 while(Im != NULL)
 {
   GausFunc(Im->Raster, gfGausDiffX, 1);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


//////////////////////////////////


class DiffYImageFilter: AImageFilter
{
public:
  DiffYImageFilter(void) {};
  virtual void ProcessFilter(Image *Im);
};


void DiffYImageFilter::ProcessFilter(Image *Im)
{
 if(Verbosity>=0) printf("\rDiffY filter ");
 while(Im != NULL)
 {
   GausFunc(Im->Raster, gfGausDiffY, 1);
   Im = Im->Next;
 }
 if(Verbosity>=0) printf("\n");
}


/////////////////////////////////////////////


int main(int argc, char *argv[])
{
Image I1, I2;
const char *InputImg=NULL, *OutputImg=NULL;
const char *s;
const char *OutFormat=NULL;
int i;
int PeelFrameNo = -1;
int Error = 0;
Image *CurrImg = &I1;
uint16_t MorphType = El8;
uint8_t QuantiseAlg = 1;
PropertyList SaveParams;

	/* Parse Arguments */
 for(i=1; i<argc; i++)
 {
   s = argv[i];
   if(s==NULL) continue;
   if(*s==0) continue;   

   if(*s=='-' || *s=='/')
   {
     if(stricmp(s+1,"Colors") == 0)
     {       
       ++i;
       if(i>=argc) break;
       int Colors = atoi(argv[i]);
       if(Colors<=0 || Colors>=65536)
       {
         printf("\n\rInvalid amount of colors: %d.", Colors);
         goto End;
       }
       new ColorsImageFilter(Colors,QuantiseAlg);
       continue;
     }

     if(strcmp(s+1,"D")==0 || stricmp(s+1,"Dilatation")==0)
     {
       new DilateImageFilter(MorphType);
       continue;
     }
     if(stricmp(s+1,"Diff") == 0)
     {       
       new DiffImageFilter();
       continue;
     }
     if(stricmp(s+1,"DiffX") == 0)
     {       
       new DiffXImageFilter();
       continue;
     }
     if(stricmp(s+1,"DiffY") == 0)
     {       
       new DiffYImageFilter();
       continue;
     }
     if(strcmp(s+1,"E")==0 || stricmp(s+1,"Erosion")==0)
     {
       new ErodeImageFilter(MorphType);
       continue;
     }
     if(strcmp(s+1,"El4") == 0)
     {
       MorphType = El4;
       continue;
     }
     if(strcmp(s+1,"El8") == 0)
     {
       MorphType = El8;
       continue;
     }

     if(stricmp(s+1,"help")==0 || strcmp(s+1,"-help")==0 || strcmp(s+1,"?")==0)
     {
       PrintHeader();
       ++i;
       if(i<argc)
       {
         if(!stricmp(argv[i],"operators"))
         {
           PrintHelpOperators();
           return(0);
         }
         if(!stricmp(argv[i],"formats"))
         {
           PrintHelpFileFormats();
           return(0);
         }
         i--;
       }       
       PrintHelp();
       return(0);
     }
     if(strcmp(s+1,"Format")==0 || strcmp(s+1,"format")==0)
     {
       ++i;
       if(i>=argc) break;
       OutFormat = argv[i];
       continue;
     }
     if(stricmp(s+1,"Gaus") == 0)
     {       
       ++i;
       if(i>=argc) break;
       int GausSize = atoi(argv[i]);
       if(GausSize <= 0)
       {
         printf("\n\rInvalid gausian size: %d.", GausSize);
         goto End;
       }
       new GausImageFilter(GausSize);
       continue;
     }
     if(stricmp(s+1,"Canny") == 0)
     {       
       ++i;
       if(i>=argc)
       {
         new CannyImageFilter(2);
         continue;
       }
       if(!isdigit(argv[i][0]) && argv[i][0]!='+' && argv[i][0]!='-')
       {
         --i;
         new CannyImageFilter(2);
         continue;
       }

       int Scale = atoi(argv[i]);
       if(Scale<=0 || Scale>255)
       {
         printf("\n\rInvalid Canny Scale: %d.", Scale);
         goto End;
       }
       new CannyImageFilter(Scale);
       continue;
     }
     if(stricmp(s+1,"Mask") == 0)
     {       
       ++i;
       if(i>=argc) break;
       int MaskSize = atoi(argv[i]);
       if(MaskSize <= 0)
       {
         printf("\n\rInvalid mask size: %d.", MaskSize);
         goto End;
       }
       new MaskImageFilter(MaskSize);
       continue;
     }
     if(stricmp(s+1,"Median") == 0)
     {       
       ++i;
       if(i>=argc) break;
       int MedianSize = atoi(argv[i]);
       if(MedianSize <= 0)
       {
         printf("\n\rInvalid mask size: %d.", MedianSize);
         goto End;
       }
       new MedianImageFilter(MedianSize);
       continue;
     }
     if(stricmp(s+1,"PeelFrame") == 0)
     {
       ++i;
       if(i>=argc) break;
       PeelFrameNo = atoi(argv[i]);
       if(PeelFrameNo <= 0)
       {
         printf("\n\rInvalid scene number: %d.", PeelFrameNo);
         goto End;
       }       
       continue;
     }
     if(stricmp(s+1,"PeelPlane") == 0)
     {       
       ++i;
       if(i>=argc) break;
       int PeelPlaneNo = atoi(argv[i]);
       if(PeelPlaneNo <= 0)
       {
         printf("\n\rInvalid plane number: %d.", PeelPlaneNo);
         goto End;
       }
       new PeelPlaneImageFilter(PeelPlaneNo);
       continue;
     }
     if(stricmp(s+1,"Quantise-Delta") == 0)
     {
       QuantiseAlg = 1;
       continue;
     }
     if(stricmp(s+1,"Quantise-Fast") == 0)
     {
       QuantiseAlg = 0;
       continue;
     }
     if(stricmp(s+1,"Threshold") == 0)
     {       
       ++i;
       if(i>=argc) break;
       int Threshold = atoi(argv[i]);
       if(Threshold <= 0)
       {
         printf("\n\rInvalid threshold value: %d.", Threshold);
         goto End;
       }
       new ThresholdImageFilter(Threshold);
       continue;
     }

     if(strcmp(s+1,"s") == 0)
     {
       Verbosity = -1;
       continue;
     }
     if(strcmp(s+1,"v") == 0)
     {
       Verbosity = +1;
       continue;
     }
     if(strcmp(s+1,"Flip")==0 || strcmp(s+1,"flip")==0)
     {
       new FlipImageFilter;
       continue;
     }
     if(strcmp(s+1,"Gray")==0 || strcmp(s+1,"gray")==0)
     {
       new GrayImageFilter;
       continue;
     }
     if(strcmp(s+1,"Invert")==0 || strcmp(s+1,"invert")==0)
     {
       new InvertImageFilter;
       continue;
     }
     if(strcmp(s+1,"Resize")==0 || strcmp(s+1,"resize")==0)
     {
       ++i;
       if(i>=argc) break;
       const char * const x_pos = strchr(argv[i],'x');
       if(x_pos==NULL)
       {
         const float ResizeScale = atof(argv[i]);
         if(ResizeScale <= 0)
         {
           printf("\n\rInvalid resize factor: %f.", ResizeScale);
           goto End;
         }
         new ResizePercentImageFilter(ResizeScale);
       }
       else
       {
         int xSize = atoi(argv[i]);
         int ySize = atoi(x_pos+1);
         if(xSize<=0 || ySize<=0)
         {
           printf("\n\rInvalid resize geometry: %s.", argv[i]);
           goto End;
         }
         new ResizeGeomImageFilter(xSize,ySize);
       }      
       continue;
     }

     if(stricmp(s+1,"SeparateR") == 0)
     {
       new SeparateChannelImageFilter(0);
       continue;
     }
     if(stricmp(s+1,"SeparateG") == 0)
     {       
       new SeparateChannelImageFilter(1);
       continue;
     }
     if(stricmp(s+1,"SeparateB") == 0)
     {
       new SeparateChannelImageFilter(2);
       continue;
     }
     if(strcmp(s+1,"SeparateIDX") == 0)
     {
       new SeparateChannelImageFilter(10);
       continue;
     }
     if(stricmp(s+1,"Separate") == 0)
     {
       ++i;
       if(i>=argc) break;
       s = argv[i];
       if(strcmp(s,"R")==0) new SeparateChannelImageFilter(0);
       else if(strcmp(s,"G")==0) new SeparateChannelImageFilter(1);
       else if(strcmp(s,"B")==0) new SeparateChannelImageFilter(2);
       else if(strcmp(s,"IDX")==0) new SeparateChannelImageFilter(10);
       else
       {
         printf("\n\rInvalid channel for separation: %s.", s);
         goto End;
       }
       continue;
     }
     if(strcmp(s+1,"JPG_QUALITY")==0 || strcmp(s+1,"QUALITY")==0)
     {
       ++i;
       if(i>=argc) break;
       PropertyItem *Prop = new PropertyItem("QUALITY",sizeof(int));
       if(Prop->Data)
       {
         *(int*)Prop->Data = atoi(argv[i]);
         SaveParams.AddProp(Prop);
       }
       else
         delete(Prop);
       continue;
     }
     if(strcmp(s+1,"COMPRESSION")==0 || strcmp(s+1,"Compression")==0  || strcmp(s+1,"compression")==0)
     {
       ++i;
       if(i>=argc) break;
       PropertyItem *Prop = new PropertyItem("COMPRESSION",sizeof(int));
       if(Prop->Data)
       {
         *(int*)Prop->Data = atoi(argv[i]);
         SaveParams.AddProp(Prop);
       }
       else
         delete(Prop);
       continue;
     }

     if(strcmp(s+1,"jpeg:optimize-coding")==0 || strcmp(s+1,"jpeg:optimize-coding=1")==0)
     {
       PropertyItem *Prop = new PropertyItem("jpeg:optimize-coding",sizeof(char));
       if(Prop->Data)
       {
         *(char*)Prop->Data = 1;
         SaveParams.AddProp(Prop);
       }
       else
         delete(Prop);
       continue;
     }
   }

   if(InputImg==NULL)
     {InputImg=s;continue;}
   else
     if(OutputImg==NULL) {OutputImg=s;continue;}

   printf("Unknown argument \"%s\"!\n", s);
  }
	/* End of reading arguments */

  PrintHeader();
  if(Verbosity>=0) printf("\rLoading");
  //MarkTime=GetInterval();

//for(i=1;i<=10;i++)
  I1 = LoadPicture(InputImg);

  if(I1.isEmpty())
  {
    printf("\n\rCannot load input picture:\"%s\"",InputImg);
    goto End;
  }
  if(Verbosity>=0) printf("\n");

  if(PeelFrameNo >= 0)
  {
    while(PeelFrameNo>0 && CurrImg!=NULL)
    {
      if(PeelFrameNo==1) break;

      PeelFrameNo--;

      Image * const PImg = CurrImg->Next;
      CurrImg->Next = NULL;
      if(CurrImg==&I1)
      {
        I1.AttachRaster(NULL);
        I1.AttachPalette(NULL);
        I1.AttachProperty(NULL);
        I1.AttachVecImg(NULL);
      }
      else
        delete  CurrImg;
      CurrImg = PImg;
    }
    if(CurrImg==NULL)
    {
      printf("\n\rCannot obtain requested scene!");
      goto End;
    }
    if(CurrImg->Next != NULL)
    {
      delete CurrImg->Next;	// This deletes whole remaining chain.
      CurrImg->Next = NULL;
    }
  }

	// Apply image filters.
  AImageFilter::Rewind();
  while(AImageFilter::First!=NULL)
  {
    AImageFilter::First->ProcessFilter(CurrImg);
    delete AImageFilter::First;
  }

	// Apply properties for a writer.
  for(i=0; i<SaveParams.PropCount; i++)
    CurrImg->AttachProperty(SaveParams.pProperties[i]);

  if(Verbosity>=0) printf("\rSaving ");
  //MarkTime=GetInterval();

  {
    const TImageFileHandler *SavePicture_XXX;
    if(OutFormat==NULL)
    {				//No format requested
      SavePicture_XXX = FindImgHandler(ReadExt(OutputImg));
    }
    else
    {
      SavePicture_XXX = FindImgHandler(OutFormat);
    }

    if(SavePicture_XXX != NULL)
    {
      if((SavePicture_XXX->Flags&FILE_FORMAT_CAP_EXIF)==0 && HasExif(CurrImg))
         AutoOrient(CurrImg);
      if((SavePicture_XXX->Flags&FILE_FORMAT_256_COLORS)!=0)
      {
        ColorsImageFilter CF(256);
        CF.ProcessFilter(CurrImg);
      }
      Error = SavePicture_XXX->SavePicture(OutputImg,*CurrImg);
    }
    else
        Error = 0xFFFF;
  }

  if(Error)
  {
    if(OutFormat && Error==0xFFFF)
      printf("\n\rNo export format:\"%s\" found.",OutFormat);
    else
      printf("\n\rCannot save output picture:\"%s\" Error %d",OutputImg,Error);
  }
  else
  {
    if(Verbosity>=0)
    {
      //printf("time : %f",abs(GetInterval()-MarkTime)/18.2);
      puts("\rOK     ");
    }
  }

End:
   return(Error);
}

